/*  
 * Volume visualization program
 * 
 * Usage: ./volvis <data set> <transfer function> <use slow rendering>
 * For the mri image use 'mri' as data set, otherwise use 'ct' (or anything else).
 * As transfer function give transfer function file, if none is given default is loaded.
 * If the final parameter is set, ray casting is used instead of texture mapping.
 * 
 * Florian Speelman & Jannis Teunissen
 */

#include <iostream>
#include <fstream>

#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkVolume16Reader.h>
#include <vtkActor.h>
#include <vtkCamera.h>

#include <vtkVolume.h>
#include <vtkVolumeProperty.h>
#include <vtkColorTransferFunction.h>
#include <vtkPiecewiseFunction.h>
#include <vtkImageShrink3D.h>

#include <vtkDataSet.h>
#include <vtkDataSetReader.h>
#include <vtkOpenGLVolumeTextureMapper3D.h>
#include <vtkFixedPointVolumeRayCastMapper.h>

#include <vtkWindowToImageFilter.h>
#include <vtkPNGWriter.h>

#include "TFedit.h"


vtkVolume16Reader * readV16Input(void);

vtkVolume16Reader * readV16Input(void) {
	// Read in the data in the ../headsq directory
	vtkVolume16Reader *v16 = vtkVolume16Reader::New();
	v16->SetDataDimensions (256,256);
	v16->SetImageRange (1,94);
	v16->SetDataByteOrderToLittleEndian();
	v16->SetFilePrefix("../fullHead/headsq");
	v16->SetDataSpacing (0.8, 0.8, 1.5);
	unsigned short mask = (1 << 15) - 1;
	v16->SetDataMask(mask);
	return v16;
}

int screenshot(vtkRenderWindow *renderWindow) {
	int magnification;
	cout << "Integer magnification? (Usefulness depends on mapper.) ";
	cin >> magnification;
	vtkWindowToImageFilter *windowToImageFilter = vtkWindowToImageFilter::New();
	windowToImageFilter->SetInput(renderWindow);
	windowToImageFilter->SetMagnification(magnification);
	vtkPNGWriter *writer = vtkPNGWriter::New();
	writer->SetFileName("screenshot.png");
	writer->SetInput(windowToImageFilter->GetOutput());
	writer->Write();
	
	writer->Delete();
	windowToImageFilter->Delete();
}

int main (int argc, char **argv)
{
	int useFastMapper = argc <= 3;
	
	// Create the renderer, the render window, and the interactor.
	vtkRenderer *aRenderer = vtkRenderer::New();
	vtkRenderWindow *renWin = vtkRenderWindow::New();
	vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
	renWin->AddRenderer(aRenderer);
	iren->SetRenderWindow(renWin);

	vtkImageShrink3D *shrink = vtkImageShrink3D::New();
	
	// Color transfer function, giving color to different tissue types
	vtkColorTransferFunction *volumeColor = vtkColorTransferFunction::New();
	
	// Scalar opacity transfer function, giving opacity for different tissue types
	vtkPiecewiseFunction *volumeScalarOpacity = vtkPiecewiseFunction::New();
	
	// Gradient opacity transfer function, see rapport for how it works
	vtkPiecewiseFunction *volumeGradientOpacity = vtkPiecewiseFunction::New();
	
	
	vtkVolume16Reader *v16;
	vtkDataSetReader *dsReader;
	
	// Read in correct dataset, and load appropriate default transferfunction
	int loadedMRI = 0;
	if(argc >= 2) {
		if(argv[1][0] == 'm') {
			dsReader = vtkDataSetReader::New();
			dsReader->SetFileName("../uvadata/MRI-Woman.vtk");
			shrink->SetInputConnection(dsReader->GetOutputPort());
			if(argc == 2) {
				readTFFile("default-MRI.tf", volumeColor, volumeScalarOpacity, volumeGradientOpacity);
			}
			loadedMRI = 1;
		}
		if(argc >= 3) {
			readTFFile(argv[2], volumeColor, volumeScalarOpacity, volumeGradientOpacity);
		}
	}
	if(!loadedMRI) {
		v16 = readV16Input();
		shrink->SetInputConnection(v16->GetOutputPort());
		if(argc < 3) {
			readTFFile("default.tf", volumeColor, volumeScalarOpacity, volumeGradientOpacity);
		}
	}
	
	// Shrink the input -- well not yet, maybe later
	shrink->SetShrinkFactors(1,1,1);

	vtkFixedPointVolumeRayCastMapper *volumeMapper;
	vtkOpenGLVolumeTextureMapper3D *fastMapper;
	vtkVolume *volume = vtkVolume::New();
	if(useFastMapper) {
		fastMapper = vtkOpenGLVolumeTextureMapper3D::New();
		fastMapper->SetInput(shrink->GetOutput());
		volume->SetMapper(fastMapper);
	} else {
		volumeMapper = vtkFixedPointVolumeRayCastMapper::New();
		volumeMapper->SetInput(shrink->GetOutput());
		volumeMapper->AutoAdjustSampleDistancesOn();
		volume->SetMapper(volumeMapper);
	}
	
	vtkVolumeProperty *volumeProperty = vtkVolumeProperty::New();
	volumeProperty->SetColor(volumeColor);
	volumeProperty->SetScalarOpacity(volumeScalarOpacity);
	volumeProperty->SetGradientOpacity(volumeGradientOpacity);
	volumeProperty->SetInterpolationTypeToLinear();
	volumeProperty->ShadeOn();
	volumeProperty->SetAmbient(0.4);
	volumeProperty->SetDiffuse(0.6);
	volumeProperty->SetSpecular(0.2);

	volume->SetProperty(volumeProperty);
	aRenderer->AddViewProp(volume);
		   
	// Define initial view of the data
	vtkCamera *aCamera = vtkCamera::New();
	aCamera->SetViewUp (0, 0, -1);
	aCamera->SetPosition (0, 1, 0);
	aCamera->SetFocalPoint (0, 0, 0);
	aCamera->ComputeViewPlaneNormal();

	aRenderer->SetActiveCamera(aCamera);
	aRenderer->ResetCamera ();
	aCamera->Dolly(1.5);

	aRenderer->SetBackground(1,1,1);
	renWin->SetSize(640, 480);
	aRenderer->ResetCameraClippingRange ();
	

	// Initialize the event loop and then start it.
	iren->Initialize();
	
	iren->SetDesiredUpdateRate(30.);
	renWin->Render();

	double isoValue;
	double rgb[3];
	int menu, n;
	do {
		//clearScreen();
		cout << endl << endl;
		cout << "\tVTK Volume Rendering menu\n\n";
		cout << "\t0. Exit\n";
		cout << "\t1. Edit color and opacity transfer functions\n";
		cout << "\t2. Load transfer functions from file\n";
		cout << "\t3. Save transfer functions to file\n";
		cout << "\t4. Go to interactive mode\n";
		cout << "\t5. Adjust image quality (performance)\n";
		cout << "\t6. Make a screenshot of current view\n";
		cout << "\n? ";
		
		cin >> menu;
		switch(menu) {
			case 1:
				//cout << "\nEdit points:\n";
				editTF(volumeColor, volumeScalarOpacity, volumeGradientOpacity);
				renWin->Render();
				break;
			case 2:
				cout << "Enter filename: ";
				{
					char filename[80];
					cin >> filename;
					readTFFile(filename, volumeColor, volumeScalarOpacity, volumeGradientOpacity);
				}
				renWin->Render();
				break;
			case 3:
				cout << "Enter filename: ";
				{
					char filename[80];
					cin >> filename;
					writeTFFile(filename, volumeColor, volumeScalarOpacity, volumeGradientOpacity);
				}
				renWin->Render();
				break;
			case 4:
				cout << "Now in interactive mode, press 'e' to exit\n";
				iren->Start();
				break;
			case 5:
				cout << "Give new shrink factor, 1 means full resolution\n";
				cin >> n;
				shrink->SetShrinkFactors(n,n,n);
				renWin->Render();
				break;
			case 6:
				cout << "Screenshot!\n";
				screenshot(renWin);
				break;
			case 0: // While loop is terminated
				cout << "Bye Bye!\n";
				break;
		}
	} while(menu <= 6 && menu >= 1);
	
	// If we would not be quitting now, it would have been good to delete the used objects.
	return 0;
}
